summaryrefslogtreecommitdiff
path: root/src/pages/blog/[...date].astro
diff options
context:
space:
mode:
Diffstat (limited to 'src/pages/blog/[...date].astro')
-rw-r--r--src/pages/blog/[...date].astro161
1 files changed, 161 insertions, 0 deletions
diff --git a/src/pages/blog/[...date].astro b/src/pages/blog/[...date].astro
new file mode 100644
index 0000000..1742baa
--- /dev/null
+++ b/src/pages/blog/[...date].astro
@@ -0,0 +1,161 @@
+---
+import type {
+ GetStaticPaths,
+ InferGetStaticParamsType,
+ InferGetStaticPropsType,
+} from "astro";
+import { getCollection } from "astro:content";
+import Base from "@layouts/Base.astro";
+import DateSelector from "@components/DateSelector.astro";
+import BlogCard from "@components/BlogCard.astro";
+import { sortLastCreated } from "@lib/collection/helpers";
+
+export const getStaticPaths = (async () => {
+ const posts = await getCollection("blog");
+
+ const archive = {
+ years: new Set<number>(),
+ monthsByYear: new Map<string, Set<number>>(),
+ daysByMonth: new Map<string, Set<number>>(),
+ postsByDate: new Map<string, typeof posts>(),
+ sortedDates: [] as string[],
+ };
+
+ const getYMD = (date: Date) => {
+ const y = date.getFullYear();
+ const m = date.getMonth() + 1;
+ const d = date.getDate();
+ return { y, m, d };
+ };
+
+ for (const post of posts) {
+ const { y, m, d } = getYMD(post.data.dateCreated);
+
+ archive.years.add(y);
+
+ if (!archive.monthsByYear.has(y.toString())) {
+ archive.monthsByYear.set(y.toString(), new Set());
+ }
+ archive.monthsByYear.get(y.toString())!.add(m);
+
+ const ym = `${y}/${String(m).padStart(2, "0")}`;
+ if (!archive.daysByMonth.has(ym)) archive.daysByMonth.set(ym, new Set());
+ archive.daysByMonth.get(ym)!.add(d);
+
+ const ymd = `${ym}/${String(d).padStart(2, "0")}`;
+ if (!archive.postsByDate.has(ymd)) archive.postsByDate.set(ymd, []);
+ archive.postsByDate.get(ymd)!.push(post);
+ }
+
+ archive.sortedDates = Array.from(archive.postsByDate.keys()).sort();
+
+ const paths = [];
+
+ const sortedYears = Array.from(archive.years).sort();
+
+ const lastYear = Math.max(...sortedYears.map(Number));
+ paths.push({
+ params: { year: undefined },
+ props: {
+ posts: posts.filter((p) =>
+ p.data.dateCreated.getFullYear() === lastYear
+ ),
+ next: undefined,
+ previous: sortedYears?.[sortedYears.length - 2],
+ years: sortedYears,
+ months: Array.from(archive.monthsByYear.get(lastYear.toString()) ?? []),
+ },
+ });
+
+ for (const y of sortedYears) {
+ const yearPosts = posts.filter((p) =>
+ p.data.dateCreated.getFullYear() === Number(y)
+ );
+ const idx = sortedYears.indexOf(y);
+ paths.push({
+ params: { year: y },
+ props: {
+ posts: yearPosts,
+ next: sortedYears?.[idx + 1],
+ previous: sortedYears?.[idx - 1],
+ years: sortedYears,
+ months: Array.from(archive.monthsByYear.get(y.toString()) ?? []),
+ },
+ });
+ }
+
+ const allMonths = Array.from(archive.monthsByYear.entries())
+ .flatMap(([year, mset]) =>
+ Array.from(mset).map((m) => `${year}/${String(m).padStart(2, "0")}`)
+ )
+ .sort();
+
+ for (const [y, months] of archive.monthsByYear) {
+ const sortedMonths = Array.from(months).sort();
+ for (const m of sortedMonths) {
+ const monthPosts = posts.filter((p) => {
+ const d = p.data.dateCreated;
+ return (
+ d.getFullYear() === Number(y) &&
+ d.getMonth() + 1 === m
+ );
+ });
+
+ const ym = `${y}/${String(m).padStart(2, "0")}`;
+ const idx = allMonths.indexOf(ym);
+
+ paths.push({
+ params: { year: ym },
+ props: {
+ posts: monthPosts,
+ next: allMonths?.[idx + 1],
+ previous: allMonths?.[idx - 1],
+ years: sortedYears,
+ months: Array.from(months).sort(),
+ days: Array.from(archive.daysByMonth.get(ym) ?? []).sort(),
+ },
+ });
+ }
+ }
+
+ for (let i = 0; i < archive.sortedDates.length; i++) {
+ const ymd = archive.sortedDates[i];
+ const [y, m] = ymd.split("/");
+ paths.push({
+ params: { year: ymd },
+ props: {
+ posts: archive.postsByDate.get(ymd) ?? [],
+ next: archive.sortedDates?.[i + 1],
+ previous: archive.sortedDates?.[i - 1],
+ years: sortedYears,
+ months: Array.from(archive.monthsByYear.get(y) ?? []).sort(),
+ days: Array.from(archive.daysByMonth.get(`${y}/${m}`) ?? []).sort(),
+ },
+ });
+ }
+
+ return paths;
+}) satisfies GetStaticPaths;
+
+export type Params = InferGetStaticParamsType<typeof getStaticPaths>;
+export type Props = InferGetStaticPropsType<typeof getStaticPaths>;
+
+const title = "Blog";
+const description = "Latest articles.";
+
+let { posts, previous, next, years, months, days } = Astro.props;
+posts = posts.sort(sortLastCreated);
+const date = posts[0].data.dateCreated as Date;
+---
+
+<Base {title} {description}>
+ <main>
+ <h2>Blogue</h2>
+ {date && <DateSelector {date} {years} {months} {days} />}
+ {posts.map((post) => <BlogCard {...post} />)}
+ <div>
+ {previous && <a href={`/blog/${Astro.props.previous}`}>Previous</a>}
+ {next && <a href={`/blog/${Astro.props.next}`}>Next</a>}
+ </div>
+ </main>
+</Base>